#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <functional>
#include "Log.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include"ThreadPool.hpp"
#include"Task.hpp"
using namespace std;

string defaultIp="0.0.0.0";
uint16_t Defaultport=8080;
const int backlog = 10; // 但是一般不要设置的太大

Log lg;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    UsageError
};

class TcpServer;   //先声明一下，防止ThreadData中不认识 TcpServer

class ThreadData
{
public:
    ThreadData(int fd,uint16_t port,string ip,TcpServer* tc):sockfd(fd),clientport(port),clientip(ip),tcsr(tc)
    {}
    int sockfd;
    uint16_t clientport;
    string clientip;
    TcpServer* tcsr;  //放入自己的this指针，方便调用成员函数(Service)
};



class TcpServer
{
public:
    TcpServer(uint16_t port=Defaultport,string ip=defaultIp):ip_(ip),port_(port)
    {}

    void ServerInit()
    {
       // 创建upd socket(套接字)
        listensock_ = socket(AF_INET, SOCK_STREAM, 0); // TCP,打开了网卡文件   
        if (listensock_ < 0)
        {
            lg(Fatal, "socket create error,socket:%d", listensock_);
            exit(SOCKET_ERR);
        }
        lg(Info, "socket create success,socket:%d,error:%s", listensock_, strerror(errno));

        // bind socket,自己的
        struct sockaddr_in local;
        bzero(&local, sizeof(local));       // 相当于memset
        local.sin_family = AF_INET;         // 地址族
        local.sin_port = htons(port_);      // 端口号
        inet_aton(ip_.c_str(), &(local.sin_addr));  //这段代码是用于将点分十进制的IP地址字符串转换为网络字节序（大端序）的32位二进制形式, //并存储到sockaddr_in结构体的sin_addr字段中
        // local.sin_addr.s_addr = INADDR_ANY;      // IP,表示绑定所有网卡，INADDR_ANY=0.0.0.0                                                                                             //

        if (bind(listensock_, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind socket error,socket:%d,error: %s", listensock_, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind socket success,socket:%d", listensock_);


        //前面都和Udp没啥区别，tcp多了一个监听listen
        //Tcp是面向连接的，服务器一般是比较“被动的”，服务器一直处于一种，一直在等待连接到来的状态
        if(listen(listensock_,backlog)<0)
        {
            lg(Fatal,"listen socket error,socket:%d,error:%s",listensock_,strerror(errno));
            exit(LISTEN_ERR);
        }
        lg(Info, "listen socket success,socket:%d",listensock_);
    }

    void Start()
    {
        ThreadPool<Task>::GetInstance()->Start();  //线程池必须放前面，不能放在循环里
        lg(Info, "tcpServer is running....");
        for(;;)
        {
            // 1. 获取新连接,accept----------拉客人
            struct sockaddr_in client;
            bzero(&client, sizeof(client));       // 相当于memset
            socklen_t len=sizeof(client);
            int sockfd=accept(listensock_,(struct sockaddr*)&client,&len);//这个sockfd用于通信,这一步是阻塞模式
            if(sockfd<0)     //listensock_错误或accept调用失败，
            {
                lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
                sleep(1); 
                continue;
            }
            uint16_t clientport=ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET,&(client.sin_addr), clientip, sizeof(clientip));  // in_addr转字符串的函数

            // 2. 根据新连接来进行通信-----------服务客人
            lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);

            // version 1 -- 单进程版
            // Service(sockfd,clientport,clientip);
            // close(sockfd);

            //多进程版，给孙子进程执行读写程序，因为孙子进程不用等待，进程的文件表都是独立的，所以在退出前，全关了
            // pid_t id=fork();
            // if(id==0)    //child
            // {
            //     close(listensock_);
            //     if(fork()>0)   
            //     {
            //         close(sockfd);   
            //         exit(0);   //父进程
            //     }
            //     //孙子进程
            //     Service(sockfd,clientport,clientip);
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);
            // // 非阻塞地回收子进程
            // while (waitpid(-1, nullptr, WNOHANG) > 0);

            // version 3 -- 多线程版本
            // pthread_t pid;
            // ThreadData* td=new ThreadData(sockfd,clientport,clientip,this);
            // pthread_create(&pid,nullptr,Routine,(void*)td);

            // version 4 --- 线程池版本
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->PushTask(t);
        }
    }

    //测试代码，收->发
    // void Service(int sockfd,uint16_t clientport,string clientip)
    // {
    //     char buffer[1024];
    //     while(true)
    //     {
    //         int n=read(sockfd,buffer,1024);  //收，阻塞模式
    //         if(n>0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << "client say# " << buffer << std::endl;
    //             std::string echo_string = "tcpserver echo# ";
    //             echo_string += buffer;

    //             write(sockfd, echo_string.c_str(), echo_string.size());  //发
    //         }
    //         else if(n==0)     //连接已关闭（TCP 对端调用了 close()）
    //         {
    //             lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else              //出错，检查 errno
    //         {
    //             lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    // }

    // static void* Routine(void* args)   //防this指针
    // {
    //     ThreadData* td=static_cast<ThreadData*>(args);
    //     int sockfd=td->sockfd;
    //     uint16_t clientport=td->clientport;
    //     string clientip=td->clientip;
    //     td->tcsr->Service(sockfd,clientport,clientip);

    //     delete td;
    //     pthread_detach(pthread_self());
    //     return nullptr;
    // }

    ~TcpServer() {}

private:
    int listensock_;  //不可用于TCP的数据传输
    uint16_t port_;
    string ip_;
};